Source code for tessif.visualize.compare

"""
:mod:`~tessif.visualize.compare` is a :mod:`tessif` interface allowing
visual comparision between data of the same type but coming different sources.
"""

import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import numpy as np
import pandas as pd
from scipy.interpolate import griddata
import collections
import ittools
from scipy.ndimage import zoom
import numbers
import itertools


[docs]def pareto2D(data, labels=[], colors=[], xy_labels=('x', 'y'), title=None, **kwargs): """ Visualize the optimal results of 2 dependend variables. Uses a 2-depth nested sequence or a (2 column MultiIndex) :class:`pandas.DataFrame` as data input. Plotting is done using :func:`matplotlib.pyplot.plot`. Parameters ---------- data: :class:`~collections.abc.Sequence`, :class:`pandas.DataFrame` 3 depth sequence of the data to be drawn. As in:: data = [([x1, ..., xN], [y1, ..., yN]), ...] Or a 2 column MultiIndex DataFrame as in:: data = pandas.DataFrame( zip([x1, ..., xN], [y1, ..., yN], ...), colums=pd.MultiIndex.from_tuples( itertools.product(['Case 1', 'Case 2'], ['x', 'y'])) ) labels: :class:`~collections.abc.Sequence`, default=[] Sequence of strings labeling the data sets in :paramref:`pareto2D.data`. If not empty a legend entry will be drawn for each item. Must be of equal length or longer than :paramref:`~pareto2D.data` colors: :class:`~collections.abc.Sequence`, default=[] :paramref:`~pareto2D.data`. Sequence of color specification string coloring the data sets in If not empty each plot will be colord accordingly. Otherwise matplotlibs default color rotation will be used. Must be of equal length or longer than :paramref:`~pareto2D.data` xy_labels: tuple, None, default=('x', 'y') 2 tuple of axis labeling strings. First entry is interpreted as x label, second as y label. All others will be ignored. Use ``None`` to not plot any axis labels title: str, default=None Title to be shown above the plot. If ``None`` no title will be drawn. kwargs: kwargs are passed to :func:`matplotlib.pyplot.plot`. Return ------- pareto2D_draw : list List of :class:`matplotlib.lines.Line2D` objects of the drawn data. Example ------- Most simple use case for drawing a pareto front (pf) >>> from tessif.visualize import compare >>> data=[[1, 2, 3, 4, 5], [10, 3, 2.5, 2.3, 2]] >>> pf=compare.pareto2D(data) IGNORE: >>> plt.draw() >>> plt.pause(.1) IGNORE .. image:: images/Compare_pf2d_simple.png :align: center :alt: alternate text 3 Pareto Fronts (pfs) in 1 plot: >>> data=[([1, 2, 3, 4, 5], [10, 3, 2.5, 2.3, 2]), ... ([0.5, 1, 4, 4.5, 5], [12, 4, 3, 2.9, 2.8]), ... ([1, 2, 3.8, 4, 5.5], [13, 4.3, 3, 2.5, 1.9])] >>> labels=['Case 1', 'Case 2', 'Case 3'] >>> pfs=compare.pareto2D(data, labels=labels, title='Pareto Front') IGNORE: >>> plt.draw() >>> plt.pause(.1) IGNORE .. image:: images/Compare_pf2d_3pfs.png :align: center :alt: alternate text 1 Pareto Front (pf) using a pandas.DataFrame and using matplotlib's kwargs: >>> import pandas as pd >>> data = pd.DataFrame( ... zip([1, 2, 3, 4, 5], [10, 3, 2.5, 2.3, 2]), ... columns=['Costs in €', 'CO$_2$']) >>> pf=compare.pareto2D(data, title='Pareto Front', ... xy_labels=tuple(data.columns), lw=10) IGNORE: >>> plt.draw() >>> plt.pause(.1) IGNORE .. image:: images/Compare_pf2d_kwargs.png :align: center :alt: alternate text 2 Pareto Fronts (pfs) using a MultiIndex pandas.DataFrame: >>> import itertools >>> data = pd.DataFrame(zip( ... [1, 2, 3, 4, 5], [10, 3, 2.5, 2.3, 2], ... [1, 2, 3.8, 4, 5.5], [13, 4.3, 3, 2.5, 1.9]), ... columns=pd.MultiIndex.from_tuples( ... itertools.product(['Case 1', 'Case 2'], ['x', 'y']))) >>> pfs=compare.pareto2D(data, title='Pareto Fronts', ... labels = list(data.columns.levels[0]), ... xy_labels=tuple(data.columns.levels[1])) IGNORE; >>> plt.draw() >>> plt.pause(4) >>> plt.close('all') IGNORE .. image:: images/Compare_pf2d_multi.png :align: center :alt: alternate text """ # Handle data input x = [] y = [] if isinstance(data, collections.abc.Sequence): # make sure data is a 3 nested sequence as in # [([x], [y]), ([x], [y]) ([x], [y])] data = ittools.nestify(ittools.itrify(data), 3) # iterate through every data_set: for pos, data_set in enumerate(data): # ... to append x and y data respectively x.append(data_set[0]) y.append(data_set[1]) elif isinstance(data, pd.DataFrame): if isinstance(data.columns, pd.core.indexes.multi.MultiIndex): for data_set in data.columns.levels[0]: x.append(data[data_set].iloc[:, 0]) y.append(data[data_set].iloc[:, 1]) else: x.append(data.iloc[:, 0]) y.append(data.iloc[:, 1]) else: raise TypeError( 'data is of type {} which could not be'.format(type(data)) + 'handled. Refer to functions docstring for viable data types') # Draw pareto plot f = plt.figure() ax = f.add_subplot(1, 1, 1) # Set title: if title: ax.set_title(title) # Set axis labels: if xy_labels: ax.set_xlabel(xy_labels[0]) ax.set_ylabel(xy_labels[1]) # do the plotting: lines = [] for pos, plot_data in enumerate(zip(x, y)): lines.append(ax.plot( # pass data plot_data[0], plot_data[1], label=labels[pos] if labels else None, c=colors[pos] if colors else None, # tweak color: **kwargs)) if labels: ax.legend() return lines
[docs]def interpolate_pareto3D( data, interpolation_type='linear', labels=[], cmaps=[], xyz_labels=('x', 'y', 'z'), title=None, wireframes=[], scatter={'c': 'r', 'marker': 'o', 's': 20}, zooming={'zoom': 1, 'order': 1}, zoom_scatter={'c': 'b', 'marker': 'o', 's': 10}, legend=True, **kwargs): """ Visualize the optimal results of 3 dependend variables. Comes down to drawing a 3D surface plot out of 3 1D arrays. Uses a 3-depth nested sequence or a (3 column MultiIndex) :class:`pandas.DataFrame` as data input. Plotting is done using `Axes3D.plot_surface <https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#mpl_toolkits.mplot3d.Axes3D.plot_surface>`_. Parameters ---------- data: :class:`~collections.abc.Sequence`, :class:`pandas.DataFrame` 3 depth sequence of the data to be drawn. As in:: data = [([x1, ..., xN], [y1, ..., yN], [z1, ..., zN]), ...] Or a 3 column MultiIndex DataFrame as in:: data = pandas.DataFrame( zip([x1, ..., xN], [y1, ..., yN], ...), colums=pd.MultiIndex.from_tuples( itertools.product(['Case 1', ..., 'Case N'], ['x', 'y', 'z'])) ) labels: :class:`~collections.abc.Sequence`, default=[] Sequence of strings labeling the data sets in :paramref:`~pareto3D.data`. If not empty a legend entry will be drawn for each item. Must be of equal length or longer than :paramref:`~pareto3D.data` cmaps: :class:`~collections.abc.Sequence`, default=[] Sequence of colormap specification strings for coloring the data sets in :paramref:`~interpolate_pareto3D.data`. If not empty each plot will be colored accordingly. Otherwise matplotlibs default color rotation will be used. Must be of equal length or longer than :paramref:`~interpolate_pareto3D.data` See `available colormaps <https://matplotlib.org/3.1.1/gallery/color/colormap_reference.html>`_ xyz_labels: tuple, None, default=('x', 'y', 'z') 3 tuple of axis labeling strings. First, second, third label are interpreted as x, y, z-label respectively. All others will be ignored. Use ``None`` to not plot any axis labels title : str, default=None Title to be shown above the plot. If ``None`` no title will be drawn. scatter: dict, default={'c':'r', 'marker': 'o', 's':20} Draw an additional scatterplot to visualize input data. To conifgurate the scatter plot provide respective `kwargs <https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.scatter.html>`_ in this dictionairy. Pass something that evaluates to ``False`` during the ``is scatter:`` call if you don't want scatter points to be drawn. (i.e. ``{}`` or ``None``) wireframes: :class:`~collections.abc.Sequence`, default=[] Sequence of bools. Visualize the data set as a wireframe plot instead of a surface plot if respective entry is``True``. Must be of equal length or longer than :paramref:`~interpolate_pareto3D.data` zooming : dict, default={zoom=1, order=1} Use the `scipy's zoom utility <https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.zoom.html>`_ to generate additional data points. Highly unscientific depending on the context used. Nonetheless quite beautifull and handy in times. zoom_scatter: dict, default={'c': 'b', 'marker': 'o', 's':10} Creates zoomed scatter points if zooming['zoom'] > 1. See also :paramref:`~interpolate_pareto3D.scatter`. legend: bool, default=True Draws a legend describing :paramref:`~interpolate_pareto3D.scatter` and :paramref:`~interpolate_pareto3D.zoom_scatter` if ``True`` and one of the beforenamed scatter plots is drawn. **kwargs: kwargs are passed to `Axes3D.plot_surface <https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#mpl_toolkits.mplot3d.Axes3D.plot_surface>`_ Return ------- pfs : list List of `Collections of 3D polygons <https://matplotlib.org/mpl_toolkits/mplot3d/api.html#mpl_toolkits.mplot3d.art3d.Poly3DCollection>`_ of the drawn data. Example ------- Most simple use case for drawing a pareto front (pf): >>> from tessif.visualize import compare >>> data=([10, 10, 2, 2, 3], [14, 2, 14, 1.8, 3], [1, 3, 3, 4.9, 2]) >>> pf=compare.interpolate_pareto3D(data) IGNORE: >>> plt.draw() >>> plt.pause(0.1) IGNORE .. image:: images/Compare_pfi3d_simple.png :align: center :alt: alternate text 3 Pareto Fronts (pfs) in 1 plot: >>> data = [([10, 10, 2, 2, 3], [14, 2, 14, 1.8, 3], [1, 3, 3, 4.9, 2]), ... ([12, 12, 4, 4, 5], [16, 4, 16, 3.8, 5], [3, 5, 5, 7, 2]), ... ([16, 16, 8, 8, 9], [26, 9, 26, 8.8, 9], [7, 9, 9, 11, 6])] >>> labels = ['Case 1', 'Case 2', 'Case 3'] >>> cmaps = ['viridis', 'magma', 'Reds'] >>> wireframes = [False, True, True] >>> pfs = compare.interpolate_pareto3D( ... data, labels=labels, cmaps=cmaps, ... title='Interpolated Pareto Fronts', wireframes=wireframes) IGNORE: >>> plt.draw() >>> plt.pause(.1) IGNORE .. image:: images/Compare_pfi3d_3pfs.png :align: center :alt: alternate text 1 Pareto Front (pf) using a pandas.DataFrame and using matplotlib's kwargs while getting rid of this pesky scatter plot: >>> import pandas as pd >>> data = pd.DataFrame( ... zip([12, 12, 4, 4, 5], [16, 4, 16, 3.8, 5], [3, 5, 5, 7, 2]), ... columns=['CO$_2$', 'Ressources Used in $ Equiv', 'Costs in €']) >>> pf = compare.interpolate_pareto3D( ... data, title='Interpolated Pareto Front', ... xyz_labels=tuple(data.columns), scatter=None) IGNORE: >>> plt.draw() >>> plt.pause(0.1) IGNORE .. image:: images/Compare_pfi3d_kwargs.png :align: center :alt: alternate text 2 Pareto Fronts (pfs) using a MultiIndex pandas.DataFrame and zoom in: >>> import itertools >>> data = pd.DataFrame( ... zip([10, 10, 2, 2, 3], [14, 2, 14, 1.8, 3], [1, 3, 3, 4.9, 2], ... [16, 16, 8, 8, 9], [26, 9, 26, 8.8, 9], [7, 9, 9, 11, 6]), ... columns=pd.MultiIndex.from_tuples( ... itertools.product(['Case 1', 'Case 2'], ['x', 'y', 'z']))) >>> pfs = compare.interpolate_pareto3D( ... data, ... title='Zoomed Interpolated Pareto Fronts', ... labels = list(data.columns.levels[0]), ... cmaps = ['jet', 'magma'], ... xyz_labels=tuple(data.columns.levels[1]), ... zooming={'zoom': 2, 'order': 1}, ... wireframes=[False, True]) IGNORE: >>> plt.draw() >>> plt.pause(4) >>> plt.close('all') IGNORE .. image:: images/Compare_pfi3d_multi.png :align: center :alt: alternate text """ # Handle data input x = [] y = [] z = [] length = 1 if isinstance(data, collections.abc.Sequence): # make sure data is a 3 nested sequence as in # [([x], [y]), ([x], [y]) ([x], [y])] data = ittools.nestify(ittools.itrify(data), 3) # iterate through every data_set: for pos, data_set in enumerate(data): # ... to append x and y data respectively x.append(data_set[0]) y.append(data_set[1]) z.append(data_set[2]) # length of data set euqals len(data). length = len(data) elif isinstance(data, pd.DataFrame): if isinstance(data.columns, pd.core.indexes.multi.MultiIndex): for data_set in data.columns.levels[0]: x.append(data[data_set].iloc[:, 0]) y.append(data[data_set].iloc[:, 1]) z.append(data[data_set].iloc[:, 2]) else: x.append(data.iloc[:, 0]) y.append(data.iloc[:, 1]) z.append(data.iloc[:, 2]) # length equals len() of one of the lists: length = len(x) else: raise TypeError( 'data is of type {} which could not be'.format(type(data)) + 'handled. Refer to functions docstring for viable data types') # handle wireframe demands: wfs = [] # alias wireframes to prevent unexpected bahaviour for i in range(length): wfs.append(wireframes[i] if wireframes else False) # Draw pareto plot # Use GridSpec for easy and intuitive subplot creation gs = mpl.gridspec.GridSpec(nrows=1, ncols=30) fig = mpl.pyplot.figure() # Create a 3D subplot in the first 30-len(data) columns ax = fig.add_subplot(gs[:-2*length], projection="3d") ax.set_proj_type('ortho') # Create a 3D subplot in the last 2*length columns cb_axes = [] for i in range(length): cb_axes.append(fig.add_subplot(gs[-2*(i+1)])) # Set title: if title: ax.set_title(title) # Set axis labels: if xyz_labels: ax.set_xlabel(xyz_labels[0]) ax.set_ylabel(xyz_labels[1]) ax.set_zlabel(xyz_labels[2]) # do the plotting: surfaces = [] for pos, plot_data in enumerate(zip(x, y, z)): # prepare the data be 3 2D arrays: linx = np.linspace( round(min(plot_data[0])), round(max(plot_data[0])), round(max(plot_data[0])) - round(min(plot_data[0]))+1) liny = np.linspace( round(min(plot_data[1])), round(max(plot_data[1])), round(max(plot_data[1])) - round(min(plot_data[1]))+1) X, Y = np.meshgrid(plot_data[0], plot_data[1]) X, Y = np.meshgrid(linx, liny) # interpolate the missing data Z = griddata( (np.array(plot_data[0]), np.array(plot_data[1])), np.array(plot_data[2]), (X, Y), method=interpolation_type) # zoom in: Xzoom = zoom(X, **zooming) Yzoom = zoom(Y, **zooming) Zzoom = zoom(Z, **zooming) # Get colormap cmap = mpl.pyplot.get_cmap(name=cmaps[pos] if cmaps else 'jet') # Normalize colormap to plot data normalize = mpl.colors.Normalize( vmin=Zzoom.min(), vmax=Zzoom.max()) # Visualize zoomed data as scatter points if an acutal zoom occured: zdata = None if zoom_scatter and zooming['zoom'] > 1: zdata = ax.scatter( Xzoom, Yzoom, Zzoom, label='Zoomed data', **zoom_scatter) # Visualize input data as scatter points: idata = None if scatter: idata = ax.scatter(X, Y, Z, label='Input data', **scatter) # Draw scatter legend if requested: if legend: handles = [] # and input data handle if used if idata: handles.append(idata) # add zoomed data handle if used if zdata: handles.append(zdata) # pass handles to legend drawing utility if handles: ax.legend(handles=handles) # 3D plot is requested be a colored wireframe plot: if wfs[pos]: # Draw the 3D surface plot wfp = ax.plot_surface( Xzoom, Yzoom, Zzoom, vmin=0, vmax=Z.max(), facecolors=cmap(normalize(Zzoom)), shade=False) wfp.set_facecolors((0, 0, 0, 0)) surfaces.append(wfp) else: # 3D plot is requested be a surface plot: surfaces.append(ax.plot_surface( # pass data Xzoom, Yzoom, Zzoom, label=labels[pos] if labels else None, cmap=cmap, norm=normalize, ** kwargs)) # Visualize input data as scatter points: if scatter: ax.scatter( xs=plot_data[0], ys=plot_data[1], zs=plot_data[2], **scatter) # Draw the colorbar mpl.colorbar.ColorbarBase(ax=cb_axes[pos], cmap=cmap, norm=normalize, label=labels[pos] if labels else None,) # Change axis so (0, 0, 0) is in the far bottom left corner: ax.set_xlim(0, max(plot_data[0])) ax.set_ylim(max(plot_data[1]), 0) return surfaces
[docs]def pareto3D(data, labels=[], cmaps=[], xyz_labels=('x', 'y', 'z'), title=None, wireframes=[], scatter={'c': 'r', 'marker': 'o', 's': 20}, zooming={'zoom': 1, 'order': 1}, zoom_scatter={'c': 'b', 'marker': 'o', 's': 10}, legend=True, **kwargs): """ Optimal results of 1 variable dependend of 2 other. Comes down to drawing a 3D plot out of 2 1D arrays and 1 2D array. Uses a 3-depth nested sequence or a list of 3 column class:`pandas.DataFrame` as data input. Plotting is done using :func:`matplotlib.pyplot.plot`. Parameters ---------- data: :class:`~collections.abc.Sequence` 3 depth sequence of the data to be drawn. As in:: data = [([x1, ..., xN], [y1, ..., yN], [z(x1, y1), ..., z(xN, y1)],[z(x1, yN), ..., z(xN, yN)]), ...] Or an sequence of :class:`pandas.DataFrame` as in:: data = [pandas.DataFrame( [z(x1, yN), ..., z(xN, yN)], colums=[i for i in x], index[i for i in y]), pandas.DataFrame( [z(x1, yN), ..., z(xN, yN)], colums=[i for i in x], index[i for i in y])] labels: :class:`~collections.abc.Sequence`, default=[] Sequence of strings labeling the data sets in :paramref:`~pareto3D.data`. If not empty a legend entry will be drawn for each item. Must be of equal length or longer than :paramref:`~pareto3D.data` cmaps: :class:`~collections.abc.Sequence`, default=[] Sequence of colormap specification strings for coloring the data sets in :paramref:`~interpolate_pareto3D.data`. If not empty each plot will be colored accordingly. Otherwise matplotlibs default color rotation will be used. Must be of equal length or longer than :paramref:`~interpolate_pareto3D.data` See `available colormaps <https://matplotlib.org/3.1.1/gallery/color/colormap_reference.html>`_ xyz_labels: tuple, None, default=('x', 'y', 'z') 3 tuple of axis labeling strings. First, second, third label are interpreted as x, y, z-label respectively. All others will be ignored. Use ``None`` to not plot any axis labels title : str, default=None Title to be shown above the plot. If ``None`` no title will be drawn. scatter: dict, default={'c':'r', 'marker': 'o', 's':20} Draw an additional scatterplot to visualize input data. To conifgurate the scatter plot provide respective `kwargs <https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.scatter.html>`_ in this dictionairy. Pass something that evaluates to ``False`` during the ``is scatter:`` call if you don't want scatter points to be drawn. (i.e. ``{}`` or ``None``) wireframes: :class:`~collections.abc.Sequence`, default=[] Sequence of bools. Visualize the data set as a wireframe plot instead of a surface plot if respective entry is``True``. Must be of equal length or longer than :paramref:`~interpolate_pareto3D.data` zooming : dict, default={zoom=1, order=1} Use the `scipy's zoom utility <https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.zoom.html>`_ to generate additional data points. Highly unscientific depending on the context used. Nonetheless quite beautifull and handy in times. zoom_scatter: dict, default={'c': 'b', 'marker': 'o', 's':10} Creates zoomed scatter points if zooming['zoom'] > 1. See also :paramref:`~interpolate_pareto3D.scatter`. legend: bool, default=True Draws a legend describing :paramref:`~interpolate_pareto3D.scatter` and :paramref:`~interpolate_pareto3D.zoom_scatter` if ``True`` and one of the beforenamed scatter plots is drawn. **kwargs: kwargs are passed to `Axes3D.plot_surface <https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#mpl_toolkits.mplot3d.Axes3D.plot_surface>`_ Return ------- pfs : list List of `Collections of 3D polygons <https://matplotlib.org/mpl_toolkits/mplot3d/api.html#mpl_toolkits.mplot3d.art3d.Poly3DCollection>`_ of the drawn data. Example ------- Most simple use case for drawing a pareto front (pf): >>> from tessif.visualize import compare >>> data=([5, 2, 1], [30, 20, 10], [[2, 3, 4], [6, 8, 12], [8, 10, 20]]) >>> pf=compare.pareto3D(data) IGNORE: >>> plt.draw() >>> plt.pause(.1) IGNORE .. image:: images/Compare_pf3d_simple.png :align: center :alt: alternate text 2 Pareto Fronts (pfs) in 1 plot: >>> data=[([5, 2, 1], [30, 20, 10], [[2, 3, 4], [6, 8, 12], [8, 10, 20]]), ... ([5, 2, 1], [30, 20, 10], [[4, 5, 6], [8, 10, 14], [10, 12, 22]])] >>> labels=['Case 1', 'Case 2'] >>> cmaps=['viridis', 'magma'] >>> wireframes=[False, True] >>> pfs=compare.pareto3D( ... data, labels=labels, cmaps=cmaps, title='Zoomed Pareto Fronts', ... wireframes=wireframes, zooming={'zoom': 2, 'order': 1}) IGNORE: >>> plt.draw() >>> plt.pause(.1) IGNORE .. image:: images/Compare_pf3d_2pfs_wire.png :align: center :alt: alternate text 1 Pareto Front (pf) using a pandas.DataFrame and using matplotlib's kwargs while getting rid of this pesky scatter plot: >>> import pandas as pd >>> x = [30, 20, 10] >>> y = [5, 2, 1] >>> data = pd.DataFrame( ... [[2, 3, 4], [6, 8, 12], [8, 10, 20]], ... index=[i for i in y], ... columns=[i for i in x]) >>> xyz_labels=['CO$_2$', 'Ressources Used in $ Equiv', 'Costs in €'] >>> pf=compare.pareto3D(data, title='Pareto Front', ... xyz_labels=xyz_labels, scatter=None) IGNORE: >>> plt.draw() >>> plt.pause(.1) IGNORE .. image:: images/Compare_pf3d_kwargs.png :align: center :alt: alternate text 2 Pareto Fronts (pfs) using a list of pandas.DataFrames: >>> x = [30, 20, 10] >>> y = [5, 2, 1] >>> xyz_labels=['CO$_2$', 'Ressources Used in $ Equiv', 'Costs in €'] >>> data = [pd.DataFrame( ... [[2, 3, 4], [6, 8, 12], [8, 10, 20]], ... index=[i for i in y], ... columns=[i for i in x]), ... pd.DataFrame( ... [[4, 5, 6], [8, 10, 14], [10, 12, 22]], ... index=[i for i in y], ... columns=[i for i in x])] >>> pfs=compare.pareto3D(data, title='Pareto Fronts', ... labels = ['Case 1', 'Case 2'], ... cmaps = ['jet', 'magma'], ... xyz_labels=xyz_labels) IGNORE: >>> plt.draw() >>> plt.pause(4) >>> plt.close('all') IGNORE .. image:: images/Compare_pf3d_2pfs.png :align: center :alt: alternate text """ # Handle data input x = [] y = [] z = [] length = 1 # docstring calls for [pd.DataFrame] but be liberal in what you accept if isinstance(data, pd.DataFrame): data = [data] if isinstance(data[0], collections.abc.Sequence): # make sure data is a 3 nested sequence as in # [([x], [y], [z]), ([x], [y], [z]) ([x], [y], [z])] data = ittools.nestify(ittools.itrify(data), 4) # iterate through every data_set: for pos, data_set in enumerate(data): # ... to append x and y data respectively x.append(data_set[0]) y.append(data_set[1]) z.append(np.array(data_set[2])) # length of data set euqals len(data). length = len(data) elif isinstance(data[0], pd.DataFrame): for data_set in data: x.append(data_set.columns.values) y.append(data_set.index.values) z.append(data_set.values) # length equals len() of one of the lists: length = len(x) else: raise TypeError( 'data is of type {} which could not be'.format(type(data)) + 'handled. Refer to functions docstring for viable data types') # handle wireframe demands: wfs = [] # alias wireframes to prevent unexpected bahaviour for i in range(length): wfs.append(wireframes[i] if wireframes else False) # Draw pareto plot # Use GridSpec for easy and intuitive subplot creation gs = mpl.gridspec.GridSpec(nrows=1, ncols=30) fig = mpl.pyplot.figure() # Create a 3D subplot in the first 30-len(data) columns ax = fig.add_subplot(gs[:-2*length], projection="3d") ax.set_proj_type('ortho') # Create a 3D subplot in the last 2*length columns cb_axes = [] for i in range(length): cb_axes.append(fig.add_subplot(gs[-2*(i+1)])) # Set title: if title: ax.set_title(title) # Set axis labels: if xyz_labels: ax.set_xlabel(xyz_labels[0]) ax.set_ylabel(xyz_labels[1]) ax.set_zlabel(xyz_labels[2]) # do the plotting: surfaces = [] for pos, plot_data in enumerate(zip(x, y, z)): # prepare the x-y data be 2D arrays: X, Y = np.meshgrid(plot_data[0], plot_data[1]) Z = plot_data[2] # Get colormap cmap = mpl.pyplot.get_cmap(name=cmaps[pos] if cmaps else 'jet') # Normalize colormap to plot data normalize = mpl.colors.Normalize( Z.min(), Z.max()) # zoom in: Xzoom = zoom(X, **zooming) Yzoom = zoom(Y, **zooming) Zzoom = zoom(Z, **zooming) # Visualize zoomed data as scatter points if an acutal zoom occured: zdata = None if zoom_scatter and zooming['zoom'] > 1: zdata = ax.scatter( Xzoom, Yzoom, Zzoom, label='Zoomed data', **zoom_scatter) # Visualize input data as scatter points: idata = None if scatter: idata = ax.scatter(X, Y, Z, label='Input data', **scatter) # Draw scatter legend if requested: if legend: handles = [] # and input data handle if used if idata: handles.append(idata) # add zoomed data handle if used if zdata: handles.append(zdata) # pass handles to legend drawing utility if handles: ax.legend(handles=handles) # 3D plot is requested be a colored wireframe plot: if wfs[pos]: # Draw the 3D surface plot wfp = ax.plot_surface( Xzoom, Yzoom, Zzoom, vmin=0, vmax=Zzoom.max(), facecolors=cmap(normalize(Zzoom)), shade=False, **kwargs) wfp.set_facecolors((0, 0, 0, 0)) surfaces.append(wfp) else: # 3D plot is requested be a surface plot: surfaces.append(ax.plot_surface( # pass data Xzoom, Yzoom, Zzoom, label=labels[pos] if labels else None, cmap=cmap, norm=normalize, ** kwargs)) # Draw the colorbar mpl.colorbar.ColorbarBase(ax=cb_axes[pos], cmap=cmap, norm=normalize, label=labels[pos] if labels else None,) # Change axis so (0, 0, 0) is in the far bottom left corner: ax.set_xlim(0, max(plot_data[0])) ax.set_ylim(max(plot_data[1]), 0) return surfaces
[docs]def best_bar(data, method=np.mean, duplicate=True, lowest=True, descriptive='', indicator_labels=[], colors=[], set_labels=[], title=None, **kwargs): r""" Compare different bar plotted data sets containing the same kind of data. Choose a best candidate depending on :paramref:`~best_bar.method`. Uses a 2-depth nested sequence or a :class:`pandas.DataFrame` as data input. Plotting is done using :func:`matplotlib.pyplot.bar`. Parameters ---------- data: :class:`~collections.abc.Sequence` or a :class:`pandas.DataFrame` 2 depth sequence of the data to be drawn. As in:: data = [(indicator1, ..., indicatorN), ...] Or a DataFrame as in:: data = pandas.DataFrame( [(indicator1, ..., indicatorN), ...], colums=['indicator1', ..., 'indicatorN]) ) method: str, functional, default=np.mean Comparative method to choose the best candidate. If an indicator(column) name is provided datasets are compared based on it, depending on :paramref:`~best_bar.lowest` If a functional is provided it is passed to `pandas.DataFrame.apply <https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.apply.html>`_ and evaluated depending on :paramref:`~best_bar.lowest`. Use ``'rmin'`` or ``'rmax'`` (as str) to evaluate min/max of the sum of the data sets. :paramref:`~best_bar.lowest` will be ignored then. Note ---- `pandas.DataFrame.apply <https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.apply.html>`_ also accepts strings of `pandas.DataFrame attributes <https://pandas.pydata.org/pandas-docs/stable/reference/frame.html>`_. For improved readability however it is recommended to state the functionals as functionals. lowest: bool, default=True Decide if lowest (``True``) or highest (``False``) method value is considered best. duplicate: bool, default=True Best candidate(s) is(are) copied/moved to the end for visual highlighting for True/False respectively. descriptive : str, default='' String describing the best candidate evaluation method above the bar plot. When default is used a descriptive is tried to be inferred from following standard descriptives: - :math:`\min\left(\sum data \right)` for ``'rmin'`` - :math:`\max\left(\sum data \right)` for ``'rmax'`` - :math:`\min\left(data \right)` for ``'min'`` - :math:`\max\left(data \right)` for ``'max'`` - :math:`\text{mean}\left(data \right)` for ``'np.mean'`` - :math:`\text{std}\left(data \right)` for ``'np.std'`` Or in case an indicator label name was passed from :paramref:`~best_bar.indicator_labels`. indicator_labels: sequence of str, default = [] Sequence of strings labeling the indicators of the :paramref:`~best_bar.data` subsets. If not empty a legend entry will be drawn for each item. Must be of equal length or longer than the nested sequences in :paramref:`~best_bar.data`. colors: sequence of color strings, default = [] color specifications for the data sets in :paramref:`~best_bar.data`. If not empty each bar will be colored accordingly. Otherwise matplotlibs default color rotation will be used. Must be of equal length or longer than the nested sequences in :paramref:`~best_bar.data` set_labels: :class:`collections.abc.Sequence`, default=[] labels for the data sets in :paramref:`bar.data`. If not empty an x-axis label will be drawn for each item. Must be of equal length or longer than :paramref:`bar.data` title: str, default=None Title to be shown above the plot. If ``None`` no title will be drawn. kwargs: dict kwargs are passed to :func:`matplotlib.pyplot.bar`. Return ------- bars : list List of :class:`~matplotlib.container.BarContainer` objects representing the drawn data. Example ------- Most simple use case for drawing a comparitive bar plot >>> from tessif.visualize import compare >>> data = [(1, 2, 2), (1, 2, 3), (3, 2, 1), (2, 2, 2)] >>> bars = compare.best_bar(data) IGNORE: >>> plt.draw() >>> plt.pause(.1) IGNORE .. image:: images/Compare_bar_simple.png :align: center :alt: alternate text Modify the evaluation method, add a nicer description and get rid of the ugly duplicates: >>> data=[(1, 2, 2, 3), (1, 2, 3, 4), (4, 3, 2, 1), (2, 3, 2, 2)] >>> bars=best_bar( ... data, method=np.std, duplicate=False, ... descriptive='standard deviation') .. image:: images/Compare_bar_noduplicate.png :align: center :alt: alternate text IGNORE: >>> plt.draw() >>> plt.pause(.1) IGNORE A simplified use case for an energy system analysis: >>> bars=best_bar( ... data, ... method=np.mean, ... lowest=False, ... indicator_labels=['Costs', 'CO$_2$', 'Ressources', 'Land Usage'], ... colors=['gold', 'black', 'blue', 'green'], ... set_labels=['Sufficiency', 'Wind', 'Storage', 'Wind&Solar'], ... title='Renewables, CHP, Storage - Northern Germany') IGNORE: >>> plt.draw() >>> plt.pause(.1) IGNORE .. image:: images/Compare_bar_es_simple.png :align: center :alt: alternate text Everybody wants to use pandas.DataFrames because they are awesome. Make sure to transform the indices and columns to lists! This Example use an indicator as evaluatoin method: >>> df=pd.DataFrame(data, ... index=['Sufficiency', 'Wind', 'Storage', 'Wind&Solar'], ... columns=['Costs', 'CO$_2$', 'Ressources', 'Land Usage']) >>> bars=best_bar( ... data, ... method='Costs', ... lowest=True, ... duplicate=False, ... indicator_labels=df.columns.tolist(), ... set_labels=df.index.tolist(), ... title='Renewables, CHP, Storage - Northern Germany') IGNORE: >>> plt.draw() >>> plt.pause(4) >>> plt.close('all') IGNORE .. image:: images/Compare_bar_es_dataframe.png :align: center :alt: alternate text """ # Is data passed as sequence ? if isinstance(data, collections.abc.Sequence): # yes, so transform it into a DataFrame for data science data = ittools.nestify(data, 2) df = pd.DataFrame( data, index=set_labels if set_labels else None, columns=indicator_labels if indicator_labels else None) # map row summing functionals to respective strings # allows distinguishing max from rmax (which is max(sum())) rfunctionals = {'rmin': min, 'rmax': max} # map indicator labels (columns) to 'label' for annotating best candidate best_column = {k: '\'{}\''.format(v) for k, v in zip(df.columns, df.columns)} # map frequently used methods to their default annotation string mannotate = { 'rmin': '$\\min\\left(\\sum data \\right)$', 'rmax': '$\\max\\left(\\sum data \\right)$', np.mean: '$mean\\left(data \\right)$', np.std: '$std\\left(data \\right)$', } # add indicator labels to themethod annotation map mannotate.update({**best_column}) # Figure out the best case: best_count = 0 best_value = 0 # method is an actual column provided: if method in df.columns: # lowest is the best ? if lowest is True: # yes, so get the lowest value best_value = df.min(axis='index')[method] else: # no, so get the highest value best_value = df.max(axis='index')[method] # extract the rows(indices) containing the best value best_data_set = df.loc[df[method] == best_value] # method is not a column provided but is it a summing functional ? elif method in rfunctionals.keys(): # yes, so select the right one: functional = rfunctionals[method] # calc sum of each row and use the rfunctional to compute best value best_value = functional(df.sum(axis='columns')) # extract the rows (indices) of which the sum equals the best value best_data_set = df.loc[df.sum(axis='columns') == best_value] else: # no its an 'apply-on-each-cell' functional if lowest is True: # compute the best value as minimum best_value = min(df.apply(method, axis='columns')) else: # compute the best value as maximum best_value = max(df.apply(method, axis='columns')) # extract the rows (indices) containing the best value best_data_set = df.loc[df.apply(method, axis='columns') == best_value] # count the number of datasets having the best value best_count = len(best_data_set.index) # add the best datasets to the end for visual highlighting df = df.append(best_data_set) # remove duplicates if requested if not duplicate: df = df.drop_duplicates(keep='last') # Draw annotated bar plot f = plt.figure() ax = f.add_subplot(1, 1, 1) # Set title: if title: ax.set_title(title) # calculate x vector the bars are plot on: x = np.arange(len(df.index)) # calculate the data_set length (nested sequence of data): dsl = len(list(zip(*df.to_numpy()))) bar_width = 1 / dsl - 0.1 # draw bars for pos, indicator in enumerate(zip(*df.to_numpy())): ax.bar( x + (pos-dsl/2) * bar_width + bar_width/2, indicator, width=bar_width, color=colors[pos] if colors else None, label=df.columns[pos] if indicator_labels else None) # plot vertical line to separte best from all plt.axvline(len(df.index)-best_count-0.5, c='k') # if a column value is considered best emphasize it by drawing a # horizontal line if method in df.columns: plt.axhline(best_value, c='k', ls='--', lw=0.5) # annotate best candidate(s): # expand yaxis so the best candidate annotiation/highest bar fits ax.set_ylim(0, max([max(best_data_set.max()) + 0.5, max(df.max())])) # figue out method annotation: # was the parameter used ? if descriptive: # yes, so just utilize the user input mannotation = descriptive elif method in mannotate: # no, so try falling back on a default description mannotation = mannotate[method] else: # no, and no default description, so just show the ugly truth: mannotation = str(method) # is best value a float ? if isinstance(best_value, float): # yes, so limit shown output to 2 decimals best_value = round(best_value, 2) # create the annotation label: annotation = 'Best candidate{}. Evaluated to \n{} using {}{}'.format( 's' if best_count > 1 else '', # plural s if not just 1 best candidate best_value, # inform user of best value by displaying it '' if method in rfunctionals # state 'lowest'/'highest' if meaningfull else 'lowest ' if lowest else 'highest ', mannotation # add the freshly constructed method string ) # draw the annotation: ax.annotate(annotation, # the freshly constructed annotation string xy=(len(df.index) - (best_count/2 + 0.5), # x position max(best_data_set.max())+0.2), # y position xytext=(0, 0), # (x,y) offset textcoords="offset points", # dimension of xy ha='center', va='bottom') # Use number of data_sets i.e len(data) as x ticks ax.set_xticks(x) # Are the data sets labeled ? if set_labels: # yes, so show the set labels on x ticks. ax.set_xticklabels(df.index) # Are the indicators labeled ? if indicator_labels: # yes, so display them as legend # Adjust legend location: # get bounding box of current plot: bbox = ax.get_position() # shrink bounding box to 80% of original width ax.set_position([bbox.x0, bbox.y0, bbox.width*0.8, bbox.height]) plt.legend( labelspacing=1, bbox_to_anchor=(1.0, 1), loc='upper left',)
[docs]def bar(data, indicator_labels=[], colors=[], set_labels=[], title=None, **kwargs): """ Simple bar plotting utility. Used for visually comparing scalar data of the same type among sets (usually parameters to compare different scenarios or models). Note ---- To automatically choose and visualize a best candidate among scenarios/models, consider using :meth:`best_bar`. Parameters ---------- data: :class:`~collections.abc.Container` or a :class:`pandas.DataFrame` 2 levels deep container of the data to be drawn. As in:: data = [(indicator1, ..., indicatorN), ...] Or a :class:`~pandas.DataFrame` as in:: data = pandas.DataFrame( [(indicator1, ..., indicatorN), ...], colums=['indicator1', ..., 'indicatorN]) ) The number of indicators (the length of the nested container or the number of columns inside the data frame) equals the number of bars to drawn per set. The number of sets is determined by the number of entries in the top level container or the number of rows inside the data frame. indicator_labels: sequence of str, default = [] Sequence of strings labeling the indicators of the :paramref:`~best_bar.data` subsets. If not empty a legend entry will be drawn for each item. Must be of equal length or longer than the nested sequences in :paramref:`~best_bar.data`. colors: sequence of color strings, default = [] color specifications for the data sets in :paramref:`~best_bar.data`. If not empty each bar will be colored accordingly. Otherwise matplotlibs default color rotation will be used. Must be of equal length or longer than the nested sequences in :paramref:`~best_bar.data` set_labels: :class:`collections.abc.Sequence`, default=[] labels for the data sets in :paramref:`bar.data`. If not empty an x-axis label will be drawn for each item. Must be of equal length or longer than :paramref:`bar.data` title: str, default=None Title to be shown above the plot. If ``None`` no title will be drawn. kwargs: dict kwargs are passed to :func:`matplotlib.pyplot.bar`. """ pass
[docs]def bar3D(data, xy_data=None, labels=[], colors=[], xyz_labels=('x', 'y', 'z'), title=None): """ Visualize a variable dependend on two others as a field of (stacked) bars. Designed for visualizing the :mod:`scalibility assessment <tessif.analyze.assess_scalability>` when comparing different energy system simulation models. TODOS: 1.) Implement all the parameters listed below 2.) Make the plot prettier 3.) Create the 2 example in the example section at the end of this docstring Parameters ---------- data: :class:`~collections.abc.Container` or a :class:`pandas.DataFrame` 2 levels deep container holding the of the data to be drawn. As in:: data = [[z(x1, y1), ... z(xN, y1,)], ... [z(xN, y1), ... z(xN, yN,)]] Or a DataFrame as in:: data = pandas.DataFrame( data = [[z(x1, y1), ... z(xN, y1,)], ... [z(xN, y1), ... z(xN, yN,)] ], colums=x_values, index=y_values) ) Warning ------- If providing data **NOT** as a :class:`pandas.DataFrame` :paramref:`~bar3D.xy_data` has to be provided. xy_data: tuple, None, default=None 2 tuple containing the data for the x and y-axis repsectively. First entry will be interpreted as x-axis data, second as y-axis data. All others will be ignored. An examplary tuples could be:: xy = ((1, 2, 3, 4, 5, 6), (2, 3, 4, 5, 1, 3, 10)) xy = (range(12), range(0, 24,2)) Warning ------- This parameter is only needed when :paramref:`~bar3D.data` is **NOT** supplied as a :class:`pandas.DataFrame`. It is ignored otherwise. labels: :class:`~collections.abc.Sequence`, default=[] Sequence of strings labeling the data sets in :paramref:`bar3D.data`. If not empty a legend entry will be drawn for each item. Must be of equal length or longer than :paramref:`~bar3D.data` colors: :class:`~collections.abc.Sequence`, default=[] Sequence of color specification string coloring the data sets in :paramref:`~bar3D.data`. If not empty each plot will be colord accordingly. Otherwise matplotlibs default color rotation will be used. Number of colors stated must be greater equal the number of stacks to be plotted. xyz_labels: tuple, None, default=('x', 'y') 3 tuple of axis labeling strings. First entry is interpreted as x label, second as y label, third as z label. All others will be ignored. Use ``None`` to not plot any axis labels title: str, default=None Title to be shown above the plot. If ``None`` no title will be drawn. Examples -------- """ # baseline example taken from https://stackoverflow.com/a/38088169 fig = plt.figure() ax = fig.add_subplot(111, projection="3d") # label and title setting needs to repsect parameters ax.set_xlabel("N") ax.set_ylabel("T") ax.set_zlabel("time/memory") # ax.set_xlim3d(0, 10) # ax.set_ylim3d(0, 10) if isinstance(data, pd.DataFrame): x = data.columns.tolist() y = data.index.tolist() z = data.to_numpy() else: x = xy_data[0] y = xy_data[1] z = data # # +BEGIN_EXAMPLE # # ----------------------------------------------------------------------- # # the 2 examples below should be used to visualize the bar3D in its # # example section in the docstring above # y = [10, 20, 30] # this is t # x = [1, 2, ] # this is n # # z(N, T) describes (z(N1, T),...,z(NN, T) # # this is the form the results come out of assess_scalability's DataFrame # # in case there is only a single measurement for each N/T combination # # z = [[1, 2], [3, 4], [4, 6]] # # this is the form the results come out of assess_scalability's DataFrame # # in case there is an array of measurements for each N/T combination # z = [[(1, 1, 1, 1, 3, 2), (1, 1, 1, 2, 6, 4)], # [(1, 1, 1, 1, 6, 4), (1, 1, 1, 2, 12, 8)], # [(1, 1, 1, 1, 12, 8), (1, 1, 1, 2, 24, 16)], ] # # ----------------------------------------------------------------------- # # END_EXAMPLE X = x * len(y) Y = [element for element in y for i in range(len(x))] # Parse z (data) dependent on it being a stacked bar plot (3 level deep # container) or a simple bar plot (2 levels deep container): if isinstance(z[0][0], numbers.Number): # z is 2 levels deep, aka a single bar plot number_of_stacked_bars = 1 Z = [list(itertools.chain.from_iterable(z))] else: # z is 3 levels deep, aka a stacked bar plot Z = list(itertools.chain.from_iterable(z)) Z = list(zip(*Z)) number_of_stacked_bars = len(Z) zpos = np.zeros(len(X)) # bar width and thickness need some adjustment dx = np.ones(len(X)) dy = np.ones(len(Y)) _zpos = zpos # the starting zpos for each bar for i in range(number_of_stacked_bars): ax.bar3d(X, Y, _zpos, dx, dy, Z[i], color=colors[i] if colors else None) # add the height of each bar to know where to start the next _zpos += Z[i] if title: ax.set_title(title) return fig
[docs]def comp_plot(data_comp_df, title=''): """ Draws hidden components / flows of all models in one figure. Designed to detect initial relationships between components. Parameters ---------- data_comp_df: pandas.MultiIndex MultiIndex, which contains all time series of the pre selected components/flows of all models title: str, default='' Title to be shown above the plot. If '' no title will be drawn. """ def trim_axs(axs, N): """ Reduce *axs* to *N* Axes. All further Axes are removed from the figure. """ axs = axs.flat for ax in axs[N:]: ax.remove() return axs[:N] keys = data_comp_df.keys().get_level_values(0).unique().to_list() figsize = (16, 8) cols = 2 if len(keys) % 2 == 0: rows = len(keys) // cols else: rows = len(keys) // cols + 1 # axes = generate_subplots(len(keys),row_wise=False) axes = plt.figure(figsize=figsize, constrained_layout=True).subplots( rows, cols, sharex='col') # t=0 if len(keys) % 2 != 0: axes = trim_axs(axes, len(keys)) lines = [] labels = [] for key, ax in zip(keys, axes.flat): # axes): ax.set_title(f'{key}', fontsize=16) # ,c = colors if colors else None) axPLot = ax.plot(data_comp_df[key].abs()) lines.extend(axPLot) labels.extend((data_comp_df[key].columns)) ax.tick_params(axis='both', which='major', labelsize=11) ax.tick_params(axis='both', which='minor', labelsize=11) ax.tick_params(axis='x', which='both', labelrotation=45) ax.grid() # ax.legend(data_comp_df[key].columns) lines = list(dict.fromkeys(lines)) labels = list(dict.fromkeys(labels)) plt.figlegend(lines, labels, loc=9, fontsize=12, ncol=len(labels)) plt.suptitle(title, fontsize=15) return plt.show()
[docs]def pearson_self_comparison(pearson_df, titel_dic_key): """ correlation matrix plot a correlation matrix for each model to analyse interrelationships with in each model Parameters ---------- pearson_df: pandas.DataFrame DataFrame containing the results of the correlation of the models with themselves. titel_dic_key: str string containing the names of the models or their components / flows. these are used for the automatic labeling of the martixes """ labels = pearson_df.columns # get abbv state names. fig = plt.figure(figsize=(14, 14)) # figure so we can add axis ax = fig.add_subplot(111) # define axis, so we can modify ax.matshow(pearson_df, cmap=plt.cm.RdYlGn) # display the matrix ax.set_xticks(np.arange(len(labels))) # show them all! ax.set_yticks(np.arange(len(labels))) # show them all! ax.tick_params(axis='x', which='both', labelrotation=90) ax.set_xticklabels(labels) # set to be the abbv (vs useless #) ax.set_yticklabels(labels) fig.suptitle('Self correlation of model {}'.format( str(titel_dic_key)), fontsize=16) # values inside the grid for (i, j), z in np.ndenumerate(pearson_df): ax.text(j, i, '{:0.01f}'.format(z), ha='center', va='center') plt.tight_layout(pad=0) return ax.figure
def memory_usage(path, parser, model): from tessif.analyze import trace_memory import numpy as np import matplotlib import matplotlib.pyplot as plt stoptime = trace_memory(path, parser, model) fig = plt.figure('Time measurement') plt.ylabel('time in s') plt.xlabel('measured steps') plt.bar(stoptime.keys(), stoptime.values()) plt.show() def simulation_time(path, parser, model): from tessif.analyze import stop_time import numpy as np import matplotlib import matplotlib.pyplot as plt stoptime = stop_time(path, parser, model) fig = plt.figure('Time measurement') plt.ylabel('time in s') plt.xlabel('measured steps') plt.bar(stoptime.keys(), stoptime.values()) plt.show()
[docs]def visualize2D_assess_scalability(scalability_results): """Wrapper for df.plot() Only necessary if assess_scalability is used with only_total=False. Otherwise the df.plot() function can be used directly.""" time_list2 = list() memory_list2 = list() for i in range(scalability_results.time.shape[0]): time_list = list() memory_list = list() for j in range(scalability_results.time.shape[1]): memory_list.append(scalability_results.memory.iloc[i, j].result) time_list.append(scalability_results.time.iloc[i, j].result) memory_list2.append(memory_list) time_list2.append(time_list) columns = scalability_results.time.columns.tolist() # es müssen der index und der column name von asses scalability übernommen werden! time_dataframe = pd.DataFrame(data=time_list2, index=scalability_results.time.index, columns=scalability_results.time.columns) memory_dataframe = pd.DataFrame(data=memory_list2, index=scalability_results.memory.index, columns=scalability_results.time.columns) # would be nice to retrieve the data also in tabular form time_dataframe.plot() memory_dataframe.plot() plt.show()
def visualize3D_assess_scalability(scalability_results): bar3D( scalability_results.memory, xyz_labels=('N', 'T', 'memory'), title="Memory Usage Results", ), bar3D( scalability_results.time, xyz_labels=('N', 'T', 'time',), title="Time Measurement Results", ) plt.show()